package com.fs.iot.service;

import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson2.JSON;
import com.fs.common.exception.AppException;
import com.fs.iot.bean.DeviceSimulationField;
import com.fs.iot.bean.GatewayConnectionInfo;
import com.fs.iot.bean.dto.DeviceSimulationDTO;
import com.fs.iot.bean.dto.GatewayDTO;
import com.fs.common.ErrorCodes;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.config.CronTask;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;

/**
 * 设备模拟服务
 *
 * @author  LiuQi 2025/3/23-20:25
 * @version V1.0
 **/
@Service
@Slf4j
public class DeviceSimulationService {
    private static final String CLIENT_ID = "qi-iot-simulation";

    @Autowired
    private ThreadPoolTaskScheduler scheduler;

    @Autowired
    private GatewayEntityService gatewayEntityService;

    @Autowired
    private DeviceSimulationEntityService simulationEntityService;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    private final Map<String, ScheduledFuture<?>> scheduledFutureMap = new ConcurrentHashMap<>(16);
    private final Map<String, MqttClient> mqttClientMap = new ConcurrentHashMap<>(16);

    @PostConstruct
    public void startAll() {
        // 系统启动时启动所有不自动执行的作业（自动执行的由其依赖任务启动）
        simulationEntityService.queryBuilder().eq("status", 1).query()
                .forEach(this::startSimulation);
    }

    /**
     * 启动模拟程序
     */
    public void startSimulation(DeviceSimulationDTO dto) {
        // 先创建mqtt连接
        GatewayDTO gateway = gatewayEntityService.findByCode(dto.getGatewayCode())
                .orElseThrow(AppException.supplier(ErrorCodes.IOT_GATEWAY_NOT_EXISTS));
        try {
            createClient(gateway);
        } catch (MqttException e) {
            throw new RuntimeException(e);
        }

        Runnable task = createRunnable(dto);
        CronTask cronTask = new CronTask(task, "0/" + dto.getInterval() + " * * * * ?");
        ScheduledFuture<?> future = scheduler.schedule(task, cronTask.getTrigger());
        scheduledFutureMap.put(dto.getDeviceCode(), future);
        log.debug("设备工况模拟程序启动成功");
    }

    /**
     * 创建mqtt连接
     */
    private void createClient(GatewayDTO gateway) throws MqttException {
        MqttClient client = mqttClientMap.get(gateway.getCode());
        if (null != client && client.isConnected()) {
            return;
        }

        GatewayConnectionInfo connectionInfo = gateway.getConnectionInfo();
        client = new MqttClient("tcp://" + connectionInfo.getHost() + ":" + connectionInfo.getPort(),
                CLIENT_ID + "_" + gateway.getId(), new MemoryPersistence());
        MqttConnectOptions connOpts = new MqttConnectOptions();
        connOpts.setCleanSession(true);
        connOpts.setConnectionTimeout(10);
        connOpts.setAutomaticReconnect(false);
        client.connect(connOpts);
        mqttClientMap.put(gateway.getCode(), client);
    }

    public void stopSimulation(String deviceCode) {
        ScheduledFuture<?> future = scheduledFutureMap.get(deviceCode);
        if (null != future) {
            future.cancel(true);
            scheduledFutureMap.remove(deviceCode);
        }

        log.info("设备模拟程序停止成功");
    }

    /**
     * 重启模拟程序
     */
    public void restartSimulation(DeviceSimulationDTO job) {
        this.stopSimulation(job.getDeviceCode());
        this.startSimulation(job);
    }

    private Runnable createRunnable(DeviceSimulationDTO job) {
        return () -> {
            // 发送模拟数据
            MqttClient client = mqttClientMap.get(job.getGatewayCode());
            if (null != client) {
                String topic = "data/" + job.getGatewayCode() + "/" + job.getDeviceCode();
                MqttMessage msg = new MqttMessage();
                msg.setPayload(createPayload(job));
                try {
                    client.publish(topic, msg);
                    log.debug("推送设备模拟数据成功");
                } catch (MqttException e) {
                    log.error("推送消息失败", e);
                }
            }
        };
    }

    /**
     * 创建模拟消息
     */
    private byte[] createPayload(DeviceSimulationDTO dto) {
        List<DeviceSimulationField> fields = dto.getFields();
        Map<String, Object> msg = new HashMap<>(16);

        fields.forEach(field -> {
            String code = field.getCode();
            String valueType = field.getValueType();
            String value = field.getValue();
            String maxValue = field.getMaxValue();

            if ("fixed".equals(valueType)) {
                msg.put(code, value);
                return;
            }

            if ("boolean".equals(field.getType())) {
                // 布尔值处理
                boolean b = RandomUtil.randomBoolean();
                msg.put(code, b);
                return;
            }

            float min = 0;
            if (StringUtils.isNumeric(value)) {
                min = Float.parseFloat(value);
            }

            float max = 100000;
            if (StringUtils.isNumeric(maxValue)) {
                max = Float.parseFloat(maxValue);
            }

            if ("int".equals(field.getType())) {
                int result = RandomUtil.randomInt((int) min, (int) max);

                if (valueType.equals("randAdd")) {
                    result += getAndStoreLastValue(dto.getDeviceCode(), code, (float) result).intValue();
                }

                msg.put(code, result);
                return;
            }

            // 小数
            float result = RandomUtil.randomFloat(min, max);

            if (valueType.equals("randAdd")) {
                result += getAndStoreLastValue(dto.getDeviceCode(), code, result);
            }

            msg.put(code, result);
        });

        return JSON.toJSONBytes(msg);
    }

    private Float getAndStoreLastValue(String deviceCode, String param, Float value) {
        HashOperations<String, String, String> hashOp = redisTemplate.opsForHash();
        String lastValue = hashOp.get(deviceCode, param);
        float lastValueFloat = 0F;
        if (null != lastValue) {
            lastValueFloat = Float.parseFloat(lastValue);
        }
        hashOp.put(deviceCode, param, String.valueOf(lastValueFloat + value));
        return lastValueFloat;
    }

    public static void main(String[] args) {
        System.out.println(Float.parseFloat("2.22"));
    }
}
